<?php

namespace App\Services\Yto;

use App\Services\Array2XML;
use App\Services\Yto\YtoClientException;
use App\Services\Yto\Bytes;

use Log;

class YtoClient
{

    /**
     * 商家密钥
     * @var
     */
    protected $partnerId = '';

    /**
     * 客户编码, 商家代码（必须与customerId一致）
     * @var
     */
    protected $clientId = '';

    /**
     * 商家代码 (由商家设置， 必须与clientID一致)
     * @var
     */
    protected $customerId;

    /**
     * 获取运单号接口地址
     * @var
     */
    protected $getMailNoUrl;




    public function __construct(Bytes $bytes)
    {

        $this->bytes = $bytes;

        $einfo = config('eorderinfo');



        $config = config('shipping.yto.config');

        $shipper = config('shipping.shipper');


        $this->partnerId = $config['partnerId'];        //商家密钥

        $this->clientId = $config['clientId'];          //客户编码, 商家代码（必须与customerId一致）

        $this->customerId = $config['customerId'];      //商家代码 (由商家设置， 必须与clientID一致)

        $this->getMailNoUrl = $config['singlePushUrl'];        //获取运单号接口地址

        // $this->shipper = '河南中原福塔电子商务有限公司';

        //发货人信息
        $this->shipper_name = $shipper['name'];

        $this->shipper_mobile = $shipper['mobile'];

        $this->shipper_phone = $shipper['phone'];

        $this->shipper_postCode = $shipper['postCode'];

        $this->shipper_prov = $shipper['prov'];

        $this->shipper_city = $shipper['city'];

        $this->shipper_address = $shipper['address'];


        //推送运单到海关

        $this->push_clientId = $config['push_clientId'];        //上游公司编号，发货方公司在KK系统中的公司编号, 测试公司编号：STD

        $this->customsID = $config['customsID'];                //必填，清关公司编号，清关公司在KK系统中的编号

        $this->dataType = $config['dataType'];                  //必填，业务类型，郑州海关：BL

        $this->logisticsCode = $config['logisticsCode'];        //必填，物流企业代码，海关备案的物流企业代码: L0007

        $this->logisticsName = $config['logisticsName'];        //必填，物流企业名称，海关备案的物流企业名称: 河南省圆通速递有限公司

        $this->platformCode = $einfo['ecpCodeCus_new'];         //必填，电商平台代码，海关备案的电商平台代码

        $this->platformName = $einfo['ecpNameCus'];             //必填，电商平台名称，海关备案的电商平台名称


        $this->key = $config['key'];                            //密钥

        $this->push_url = $config['push_url'];                  //订单信息请求URL
    }


    /**
     * 获取圆通单号
     * @param  [type] $order [订单信息]
     * @return [type]        [description]
     */
    public function getYtoBillNumber($order)
    {

        //数据
        $string  = "<RequestOrder>";
        $string  .= "<clientID>".$this->clientId."</clientID>";                 //必填，商家代码（必须与customerId一致）
        $string  .= "<logisticProviderID>YTO</logisticProviderID>";             //必填，物流公司ID
        $string  .= "<customerId>".$this->customerId."</customerId>";           //必填，商家代码 (由商家设置， 必须与clientID一致)
        $string  .= "<txLogisticID>".$order->order_sn."</txLogisticID>";        //必填，物流订单号
        $string  .= "<tradeNo></tradeNo>";                                      //非必填，业务交易号（可选）
        $string  .= "<totalServiceFee>0.0</totalServiceFee>";                   //非必填，总服务费[COD] （暂时没有使用，默认为0.0）
        $string  .= "<codSplitFee>0.0</codSplitFee>";                           //非必填，物流公司分润[COD] （暂时没有使用，默认为0.0）
        $string  .= "<orderType>1</orderType>";                                 //必填，订单类型(0-COD,1-普通订单,2-便携式订单3-退货单)
        $string  .= "<serviceType>0</serviceType>";                             //必填，服务类型(1-上门揽收, 2-次日达 4-次晨达 8-当日达,0-自己联系)。默认为0
        $string  .= "<flag>1</flag>";                                           //非必填，订单flag标识，默认为 0，暂无意义
        $string  .= "<sender>";                                                 //发件人
        $string  .= "<name>".$this->shipper_name."</name>";                     //必填，用户姓名
        $string  .= "<postCode>".$this->shipper_postCode."</postCode>";         //非必填，用户邮编
        $string  .= "<phone>".$this->shipper_phone."</phone>";                  //非必填，用户电话，包括区号、电话号码及分机号，中间用“-”分隔；
        $string  .= "<mobile>".$this->shipper_mobile."</mobile>";               //非必填，用户移动电话， 手机和电话至少填一项
        $string  .= "<prov>".$this->shipper_prov."</prov>";                     //必填，用户所在省
        $string  .= "<city>".$this->shipper_city."</city>";                     //必填，用户所在市县（区），市区中间用英文“,”分隔；注意有些市下面没有区
        $string  .= "<address>".$this->shipper_address."</address>";            //必填，用户详细地址
        $string  .= "</sender>";
        $string  .= "<receiver>";
        $string  .= "<name>".$order->consignee."</name>";
        $string  .= "<postCode></postCode>";
        $string  .= "<phone>".$order->mobile."</phone>";
        $string  .= "<prov>".$order->province."</prov>";
        $string  .= "<city>".$order->city.','.$order->district."</city>";
        $string  .= "<address>".$order->address."</address>";
        $string  .= "</receiver>";
        $string  .= "<sendStartTime></sendStartTime>";                          //非必填，物流公司上门取货时间段
        $string  .= "<sendEndTime></sendEndTime>";                              //非必填，物流公司上门取货时间段
        $string  .= "<goodsValue>".$order->pay_price."</goodsValue>";           //非必填，商品金额，包括优惠和运费，但无服务费
        $string  .= "<itemsValue>".$order->pay_price."</itemsValue>";           //非必填，货物价值
        $string  .= "<items>";

        foreach ($order->orderGoods as $key => $goods) {

            $string  .= "<item>";
            $string  .= "<itemName>".$goods->goods_name."</itemName>";          //必填，商品名称
            $string  .= "<number>".$goods->buy_number."</number>";              //必填，商品数量
            $string  .= "<itemValue>".$goods->goods_real_price."</itemValue>";  //非必填，商品单价（两位小数）
            $string  .= "</item>";
        }

        $string  .= "</items>";
        $string  .= "<insuranceValue>0.0</insuranceValue>";                     //非必填，保值金额（暂时没有使用，默认为0.0）
        $string  .= "<special>0</special>";                                     //非必填，商品类型（保留字段，暂时不用）
        $string  .= "<remark>goods</remark>";                                   //非必填，备注
        $string  .= "</RequestOrder>";
        //echo "$string.$this->partnerId:";
        //print_r($string.$this->partnerId);

        $data_digest= base64_encode($this->bytes->toStr($this->bytes->getBytes(md5($string.$this->partnerId,true))));
        //echo "$data_digest:";
        //print_r($data_digest);
        $tt = $string.$this->partnerId;
        //echo $tt;
        //전송할 값들
        $data=array();
        $data['logistics_interface']=$string;//urlencode();
        $data['data_digest']=($data_digest);
        $data['clientId']=$this->clientId;
        /*echo "<br><pre>";print_r("<br>********************************************************************");
        print_r($data);print_r("<br>********************************************************************");
        echo "</pre><br>";*/


        //print_r(http_build_query($data));
        //print_r($data);
        Log::info('获取圆通单号请求数据：');
        Log::info($data);

        return $this->makeRequest($this->getMailNoUrl, $data);
    }


    public function pushSingle($order, $current_time, $logisticsNo)
    {
        //获取圆通单号
        // $mailNo = $this->getYtoBillNumber($order);


        //批次号
        // $batchNumbers = strtotime($order->add_time);
        $batchNumbers = strtotime($current_time);
        $nowHour = date('H',$batchNumbers);
        $batchNumbers = date('Ymd',$batchNumbers);
        if($nowHour>=0 && $nowHour<12){
            $batchNumbers .= '1';
        }elseif($nowHour>=12 && $nowHour<24){
            $batchNumbers .= '2';
        }

        $weight = 0;//重量
        $quantity = '';//商品总数量
        $net_weight = 0.00;     //净重
        $worth = 0.00;          //商品总货值

        foreach ($order->orderGoods as $k => $goods) {

            $buy_number = $goods->buy_number;
            $weight= $weight+$goods->weight*$buy_number;
            $quantity = $quantity+$buy_number;
            $net_weight +=$goods->buy_number*$goods->net_weight;
            $worth += $goods->goods_real_price;
        }

        //推送
        $seqNo=rand(100000000000,999999999999).date('Ymd');
        $sendTimeStamp=date('Y-m-d H:i:s');
        $orderNo=rand(100000000000,999999999999);
        $declarationDate=date('Y-m-d');

        $xml = '<?xml version="1.0" encoding="utf-8"?>
                     <Message>
                         <Header>';
        $xml .=             '<seqNo>'.$seqNo.'</seqNo>';                                          //必填，客户端的消息流水号
        $xml .=             '<sendTimeStamp>'.$sendTimeStamp.'</sendTimeStamp>';                  //必填，客户端消息发送时间戳
        $xml .=         '</Header>
                         <RequestOrder>';
        $xml .=             '<clientID>'.$this->push_clientId.'</clientID>';                      //必填，上游公司编号，发货方公司在KK系统中的公司编号, 测试公司编号：STD
        $xml .=             '<logisticProviderID>YTO</logisticProviderID>';                       //必填，物流公司编号，物流公司在KK系统中的公司编号，默认圆通：YTO
        $xml .=             '<customsID>'.$this->customsID.'</customsID>';                        //必填，清关公司编号，清关公司在KK系统中的编号
        $xml .=             '<dataType>'.$this->dataType.'</dataType>';                           //必填，业务类型，郑州海关：BL
        $xml .=             '<logisticsCode>'.$this->logisticsCode.'</logisticsCode>';            //必填，物流企业代码，海关备案的物流企业代码: L0007
        $xml .=             '<logisticsName>'.$this->logisticsName.'</logisticsName>';                       //必填，物流企业名称，海关备案的物流企业名称: 河南省圆通速递有限公司
        $xml .=             '<logisticsCodeCiq></logisticsCodeCiq>';                              //非必填，物流企业代码，国检备案的物流企业代码:4100300005
        $xml .=             '<logisticsNameCiq></logisticsNameCiq>';                              //非必填，物流企业名称，国检备案的物流企业名称
        $xml .=             '<totalLogisticsNo>'.$logisticsNo.'</totalLogisticsNo>';                              //非必填，总运单号，当时是为了区分这三层运单的情形(即有总运单、分运单、面单)，实际中一般只有总运单和运单，是两层结构。所以物流商一般只需指定总运单号和运单号即可。
        $xml .=             '<subLogisticsNo></subLogisticsNo>';                                  //非必填，分运单号，当时是为了区分这三层运单的情形(即有总运单、分运单、面单)，实际中一般只有总运单和运单，是两层结构。所以物流商一般只需指定总运单号和运单号即可。
        $xml .=             '<logisticsNo>'.$logisticsNo.'</logisticsNo>';                                        //非必填，物流企业运单号，当时是为了区分这三层运单的情形(即有总运单、分运单、面单)，实际中一般只有总运单和运单，是两层结构。所以物流商一般只需指定总运单号和运单号即可。
        $xml .=             '<orderNo>'.$order->order_sn.'</orderNo>';                            //必填，订单编号，发货方所在电商平台的订单编号
        $xml .=             '<platformCode>'.$this->platformCode.'</platformCode>';               //必填，电商平台代码，海关备案的电商平台代码
        $xml .=             '<platformName>'.$this->platformName.'</platformName>';               //必填，电商平台名称，海关备案的电商平台名称
        $xml .=             '<platformCodeCiq></platformCodeCiq>';                                //非必填，电商平台代码，国检备案的电商平台代码
        $xml .=             '<platformNameCiq></platformNameCiq>';                                //非必填，电商平台名称，国检备案的电商平台名称
        $xml .=             '<trackNo></trackNo>';
        $xml .=             '<trackStatus></trackStatus>';
        $xml .=             '<shipping>'.$this->shipper_name.'</shipping>';                       //必填，发货人名称，
        $xml .=             '<shippingAddress>'.$this->shipper_address.'</shippingAddress>';      //必填，发货人地址
        $xml .=             '<shippingTelephone>'.$this->shipper_phone.'</shippingTelephone>';    //非必填，发货人电话
        $xml .=             '<shippingZipCode>'.$this->shipper_postCode.'</shippingZipCode>';     //非必填，发货人邮编
        $xml .=             '<shippingCountryCiq>142</shippingCountryCiq>';                       //必填，发货人所在国（检），142
        $xml .=             '<shippingCountryCus>142</shippingCountryCus>';                       //必填，发货人所在国（关），142
        $xml .=             '<consignee>'.$order->consignee.'</consignee>';                       //必填，收货人名称
        $xml .=             '<consigneeAddress>'.$order->address.'</consigneeAddress>';           //必填，收货人地址
        $xml .=             '<consigneeTelephone>'.$order->mobile.'</consigneeTelephone>';        //必填，收货人电话
        $xml .=             '<consigneeZipCode>100000</consigneeZipCode>';                        //必填，收货人邮编
        $xml .=             '<consigneeCountryCiq>156</consigneeCountryCiq>';                     //必填，收货人所在国（检），142
        $xml .=             '<consigneeCountryCus>142</consigneeCountryCus>';                     //必填，收货人所在国（关），142
        $xml .=             '<idType>1</idType>';                                                 //必填，证件类型，1-身份证 2-军官证 3-护照 4-其他
        $xml .=             '<idNumber>'.trim($order->id_card).'</idNumber>';                     //必填，证件号码，根据证件类型填写相应的证件号码
        $xml .=             '<declarationDate>'.$declarationDate.'</declarationDate>';            //非必填，申报日期/进口日期，可以不填。（格式：yyyy-mm-dd）
        $xml .=             '<internationalFreight></internationalFreight>';                      //非必填，跨境运费，境外运输的费用
        $xml .=             '<domesticFreight></domesticFreight>';                                //非必填，境内运费，国内派送的费用
        $xml .=             '<supportValue></supportValue>';                                      //非必填，保价金额，针对一个运单里的所有商品的保价金额
        $xml .=             '<worth>'.$worth.'</worth>';                                          //必填，货品价值，Double(10, 2)，一个运单里所有商品的价值
        $xml .=             '<currCode>142</currCode>';                                           //非必填，币制，海关给出的货币编码数据字典，没有，可不填，142
        $xml .=             '<grossWeight>'.number_format(trim($weight),2).'</grossWeight>';      //必填，毛重，整个包裹的重量，1
        $xml .=             '<quantity>'.$quantity.'</quantity>';                                 //必填，数量，一个运单就是一个包裹，不存在多个包裹。是包裹内件数量
        $xml .=             '<packageTypeCiq>4M</packageTypeCiq>';                                //必填，包装种类（检），包装种类（参考数据字典的国检包装种类代码）
        $xml .=             '<packageTypeCus>2</packageTypeCus>';                                 //非必填，包装种类（关），包装种类（参考数据字典的海关包装种类代码）
        $xml .=             '<packNum>1</packNum>';                                               //非必填，件数，一个运单包含的包裹数，一般是1个。
        $xml .=             '<netWeight>'.number_format(trim($net_weight),2).'</netWeight>';      //必填，净重，商品的重量，除掉包装箱的重量。Double(10, 2)
        $xml .=             '<goodsName>商品描述</goodsName>';                                    //必填，商品名称，包裹中商品整体的描述，一个包裹中有多个不同商品，可以用单个商品名称替代或者整体描述
        $xml .=             '<deliveryMethod></deliveryMethod>';                                  //非必填，交货方式，FOB、CIF等
        $xml .=             '<transportationMethod>7</transportationMethod>';                     //必填，运输方式（检），国检运输方式（参考数据字典运输方式代码），7:保税区
        $xml .=             '<shipCode>20</shipCode>';                                            //必填，运输工具（检），国检运输工具（参考数据字典运输工具代码），20:船舶
        $xml .=             '<shipName></shipName>';                                              //非必填，运输工具名称，一般填公路运输、铁路运输的多。
        $xml .=             '<destinationPort></destinationPort>';                                //非必填，装运港/指运港，出口指定指运港，进口指定国外的装运港（参考数据字典的装运港和指运港代码）
        $xml .=             '<ieType>I</ieType>';                                                 //必填，进出口标志，I-进口 E-出口
        $xml .=             '<stockFlag>2</stockFlag>';                                           //必填，集货/备货，1-集货 2-备货
        $xml .=             '<batchNumbers>'.$batchNumbers.'</batchNumbers>';                     //必填，批次号，方便查询和报关行申报
        $xml .=             '<tradeCountryCiq>156</tradeCountryCiq>';                             //必填，贸易国别（检），进口是发货人国家的国检编码，出口是收货人国家的国检编码（参考数据字典国检的国家代码）
        $xml .=             '<tradeCountryCus>142</tradeCountryCus>';                             //非必填，贸易国别（关），进口是发货人国家的海关编码，出口是收货人国家的海关编码（参考数据字典海关的国家代码）
        $xml .=             '<agentCode></agentCode>';                                            //非必填，代理企业，一般是报关行，或者有申报权限的电商或物流企业在海关备案的企业代码
        $xml .=             '<agentName></agentName>';                                            //非必填，代理企业名称,一般是报关行，或者有申报权限的电商或物流企业在海关备案的企业名称
        $xml .=             '<agentCodeCiq></agentCodeCiq>';                                      //非必填，代理企业代码，一般是报关行，或者有申报权限的电商或物流企业在国检备案的企业代码
        $xml .=             '<agentNameCiq></agentNameCiq>';                                      //非必填，代理企业名称，一般是报关行，或者有申报权限的电商或物流企业在国检备案的企业名称
        $xml .=             '<billType>3</billType>';                                             //必填，总运单分运单标志，1-总运单 2-分运单 3-运单
        $xml .=             '<modifyMark>1</modifyMark>';                                         //必填，操作类型，1-新增 2-修改 3-删除
        $xml .=             '<customsField>299201</customsField>';                                //非必填，场站代码
        $xml .=             '<note></note>';                                                      //非必填，备注
        $xml .=             '<reserve1></reserve1>';                                              //非必填，备用字段1
        $xml .=             '<reserve2></reserve2>';                                              //非必填，备用字段2
        $xml .=             '<reserve3></reserve3>';                                              //非必填，备用字段3
        $xml .=             '<reserve4></reserve4>';                                              //非必填，备用字段4
        $xml .=             '<reserve5></reserve5>';                                              //非必填，备用字段5
        $xml .=          '</RequestOrder>
                     </Message>';

        Log::info('推送圆通运单请求报文：'.$xml);


        $result = &$this->sendData($xml, $this->key, $this->push_url);

        return $result;

    }


    /**
     * 构建请求
     * @param $data
     * @return mixed|string
     */
    private function makeRequest($url, $data)
    {

        try {
            $curl = curl_init();

            //서버 요청주소
            // curl_setopt($curl, CURLOPT_URL, 'http://112.64.239.247:7800/web/CommonOrderModeBServlet.action');
            curl_setopt($curl, CURLOPT_URL, $url);
            curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($curl, CURLOPT_POST, 1 );
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);


            //var_dump($curl);

            $tmpInfo = curl_exec ($curl);
            // $response = curl_exec($ch);

            curl_close ($curl);

        }catch (\Exception $e){

            throw new YtoClientException($e->getMessage());
        }

        Log::info('获取圆通单号响应报文：'.$tmpInfo);

        // $repsonXmlObj = simplexml_load_string($response);
        $responseXmlObj = simplexml_load_string($tmpInfo);

        /*foreach ($repsonXmlObj->Result as $rrs){
            $logisticsNO=$rrs->logisticsNo;
            if($rrs->Status=='Success'){
                return $logisticsNO;
            }
            else{
                throw new YtoClientException($rrs->Remark);
            }
        }*/
        if ($responseXmlObj->success=='true') {

            return $responseXmlObj->mailNo;
        }else{

            throw new YtoClientException($responseXmlObj->reason);
        }


        //get_object_vars($xml)
        /*$success = strval($xml->success);

        echo "<pre>";print_r("********************************************************************<br>");
        print_r($xml);print_r("<br>********************************************************************");
        echo "</pre>";*/

    }



    /**
     * 描述 : 发送数据到圆通接口
     * 参数 :
     *     &xml : 通信的xml字符串
     *     &key : 通信密钥
     *     &url : 通信地址
     * 返回 :
     *      失败false, 成功服务器响应结果
     * 作者 : Edgar.lee
     */
    function &sendData(&$xml, &$key = 'INTERFACESTANDARDENCRYPTKEY2014', &$url ='http://180.153.190.90/globalunion/outcall/blcWayBill')
    {
        $dosCommand = "java -jar ThreeDes.jar enc {$key} " . base64_encode($xml);                                           //DOS 命令
        if (PHP_OS === 'WINNT' && version_compare(PHP_VERSION, '5.3.0', '<')) {                                             //兼容win php < 5.3
            $dosCommand = '"' . $dosCommand . '"';
        }

        $des = base64_encode(exec($dosCommand));                                                                            //执行DOS
        $post = array(                                                                                                      //生成POST
            'logistics_interface' => &$des,
            'data_digest' => base64_encode(md5("STD{$des}STD", true)),
            'clientID' => 'STD'
        );

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post);                                                                         //加密的POST数据
        $data = curl_exec($ch);

        curl_close($ch);

        /*$data = '<?xml version="1.0" encoding="UTF-8"?>
        <Message>
            <Header>
                <SeqNo></SeqNo>
                <SendTimeStamp>2017-01-19 10:01:29:355</SendTimeStamp>
                <success>true</success>
            </Header>
            <ResponseOrder>
                <ClientID>STD</ClientID>
                <LogisticProviderID>YTO</LogisticProviderID>
                <OrderID>858591402650</OrderID>
                <ResultFlag>1</ResultFlag>
                <ErrCode>0</ErrCode>
                <ErrMsg></ErrMsg>
            </ResponseOrder>
            <leen>
                <name>leedaning</name>
            </leen>
        </Message>';*/

        Log::info('推送圆通运单响应报文：'.$data);

        $responseXmlObj = simplexml_load_string($data);

        if (@$responseXmlObj->Header->success=='true') {

            $result = 'true';

            return $result;
        }else{

            throw new YtoClientException($responseXmlObj->ResponseOrder->ErrMsg);
        }

        // return $data;
    }


}